import { isObject, def } from '../util/index'
import { arrayMethods } from './array'

class Observer {
    constructor(value){

        // 给每一个监控过的对象增加一个属性
        // value.__ob__ = this 这样写就无限递归引用了
        def(value, '__ob__', this)

        // vue的数据层次过多，需要递归的去解析对象中的属性，并以此增加set和get方法
        if(Array.isArray(value)){
            // 如果是数组的话并不对索引进行观测，太影响性能
            // 重写数组的push shift unshift 
            value.__proto__ = arrayMethods
            // 判断如果数组中是对象的话在进行观测
            this.observeArray(value)
        } else {
            this.walk(value)
        }
    }

    observeArray(value){
        value.forEach(v => {
            this.walk(v)
        });
    }

    walk(data){
        let keys = Object.keys(data) //[name, age, address]
        keys.forEach(key => {
            defineReactive(data, key, data[key]) // vue核心方法，定义响应式
        });
    }
}

/**
 * vue核心方法，定义响应式
 * @param {*} data 
 * @param {*} key 
 * @param {*} value 
 */
function defineReactive(data, key, value){
    // 递归
    observe(value)
    Object.defineProperty(data, key, {
        get(){
            return value
        },
        set(newValue){
            if(newValue === value) return
            // 如果当设置了新的对象，继续递归劫持
            observe(value)
            console.log("数值发生变化", key)
            value = newValue
        }
    })
}

// 把data中的数据都使用 Object.defineProperty重新定义
// Object.defineProperty 不能兼容IE8及以下版本 所以VUE2不支持IE8
export function observe(data){
    let isObj = isObject(data)
    if(!isObj){
        return
    }
    return new Observer(data)
}